#include	"alp_global_vars.h"
#include	"config_parser.h"
//------------------------------------------------------------------------------------
struct timeval startTime, endTime, performance_start_time, performance_end_time, tv1, tv2, tv3;
int acceptorID, proposerID, self_sid, successor_sid, SUBMIT_PER_WAKE, PROP_PER_WAKE;
int lowestDelivered = -1, gapEnd = -1, digit = 0;
bool COORDINATOR = false, rtag = false;

inst *data;
proposer_msg *p_msgs[MAX_ACCEPTORS];
int is_proposer = 0;
paxos_data* paxos_instances;

connection acceptor_connections[1];
int acceptor_connection_cnt = 0;
inst *new_data;
const char *config_file_path;



//------------------------------------------------------------------------------------
void main_initialize();
void common_event_init();
void initialize();
void validate_input_parameters(int argc, const char * argv[]);
void clean_exit(int sig);
void enable_ctrl_c_handler();
//------------------------------------------------------------------------------------
int main(int argc, const char *argv[]) {
	int status;

	validate_input_parameters(argc, argv);
	parse_configuration(config_file_path, acceptorID, proposerID, -1);
	enable_ctrl_c_handler();

	successor_sid = GET_ACCEPTOR_SUCCESSOR_SID_RING(acceptorID);
	self_sid = GET_ACCEPTOR_SELF_SID_RING(acceptorID);

	event_init();
	p_initialize();
	initialize();
	common_event_init();

	printf("-----------------------------------------------------------\n");
	if (get_config_info()->self_acceptor_id == 0) {
		COORDINATOR = true;
		//--------------------Events------------------------------------------
		//		//let's make this a socket event.
		event_set(inst_propose_timeout_eve, successor_sid, EV_WRITE | EV_PERSIST, prepare_fresh_instances_timer_event, NULL);
		status = event_add(inst_propose_timeout_eve, NULL);
		assert(status >= 0);

		//		evtimer_set(inst_propose_timeout_eve, prepare_fresh_instances_timer_event, inst_propose_timeout_eve);
		//		status = evtimer_add(inst_propose_timeout_eve, &tv3);
		//		assert(status >= 0);
	}
	event_dispatch();
	return 0;
}

void common_event_init() {

	recv_ring_sock_eve = (struct event*) malloc(sizeof(struct event));
	inst_propose_timeout_eve = (struct event*) malloc(sizeof(struct event));
	proposal_propose_timeout_eve = (struct event*) malloc(sizeof(struct event));
	print_stat_timeout_eve = (struct event*) malloc(sizeof(struct event));

	tv1.tv_sec = 1;
	tv1.tv_usec = 0;

	int status;

	gettimeofday(&startTime, NULL);
	gettimeofday(&performance_start_time, NULL);

	evtimer_set(print_stat_timeout_eve, compute_performance, print_stat_timeout_eve);
	status = evtimer_add(print_stat_timeout_eve, &tv1);
	assert(status >= 0);

	event_set(recv_ring_sock_eve, self_sid, EV_READ | EV_PERSIST, recv_msg_recognize_type, NULL);
	status = event_add(recv_ring_sock_eve, NULL);
	assert(status >= 0);

	if (is_proposer == 1) {
		evtimer_set(proposal_propose_timeout_eve, generate_proposer_msg_timer_event, proposal_propose_timeout_eve);
		status = evtimer_add(proposal_propose_timeout_eve, &tv2);
		assert(status >= 0);
		//		event_set(proposal_propose_timeout_eve, successor_sid, EV_WRITE | EV_PERSIST, generate_proposer_msg_timer_event, NULL);
		//		status = event_add(proposal_propose_timeout_eve, NULL);
		//		assert(status >= 0);


	}

}
void initialize() {

	N = get_config_info()->total_num_acceptors;

	alp_interval_performance = malloc(sizeof(alp_performance));
	alp_next_performance_index = 0;
	alp_nulify_performance(alp_interval_performance);

	data = (inst*) malloc(sizeof(inst) * INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers);
	new_data = (inst*) malloc(sizeof(inst));
	paxos_instances = (paxos_data*) malloc(sizeof(paxos_data) * INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers);
	p_msg = (proposer_msg*) malloc(sizeof(proposer_msg));

	int i;
	for (i = 0; i < get_config_info()->total_num_proposers * INTERNAL_BUFFER_SIZE; i++) {
		data[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].deliver = false;
		data[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].received = false;
		data[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].highestDeliverable = -1;
		data[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].packet.p_msgs[0].msg_id = 0;
	}

	for (i = 0; i < get_config_info()->total_num_acceptors; i++)
		p_msgs[i] = (proposer_msg*) malloc(sizeof(proposer_msg) * INTERNAL_BUFFER_SIZE);

	if (!COORDINATOR) {
		for (i = 0; i < INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers; i++) {
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].rnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].vrnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].vval = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].crnd = -1;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].cval = -1;
		}
	} else {
		for (i = 0; i < INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers; i++) {
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].rnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].vrnd = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].vval = 0;
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].crnd = 1;// before =0
			paxos_instances[(i % INTERNAL_BUFFER_SIZE * get_config_info()->total_num_proposers)].cval = 0;
		}
	}
}
void validate_input_parameters(int argc, const char * argv[]) {

	if (argc != 11) {

		printf("Incompatible call to this function. Try Again.! (argc: %d)\n"
			"<1. Acceptor ID>\n"
			"<2. Proposer ID >\n"
			"<3. Config file >\n"
			"<4. Sending Interval(Microseconds)>\n"
			"<5. Number of Packets/Interval (instances)>\n"
			"<6. Number of packets/Interval (proposing)>\n"
			"<7. File to store the performance>\n"
			"<8. Latency file>\n"
			"<9. SD, AVG file>\n"
			"<10. Is this a proposer?(1=y/0=n)>\n", argc);

		exit(-1);

	} else {

		acceptorID = atoi(argv[1]);
		proposerID = atoi(argv[2]);
		config_file_path = argv[3];
		tv3.tv_usec = atoi(argv[4]);
		tv2.tv_usec = atoi(argv[4]);
		SUBMIT_PER_WAKE = atoi(argv[5]);
		PROP_PER_WAKE = atoi(argv[6]);
		alp_performance_file = argv[7];
		alp_latency_file = argv[8];
		alp_sd_avg_file = argv[9];
		is_proposer = atoi(argv[10]);


		tv3.tv_sec = 0;
		tv2.tv_sec = 0;
	}
}

void clean_exit(int sig) {
	signal(sig, clean_exit);

	printf("Caught exit signal\n");
	alp_store_performance_in_file(alp_performance_file, alp_latency_file, alp_sd_avg_file, alp_next_performance_index);

	close(get_config_info()->server_infos[acceptorID].self_sid);
	close(get_config_info()->server_infos[acceptorID].successor_sid);
	free(data);
	int i;
	for (i = 0; i < get_config_info()->total_num_acceptors; i++)
		free(p_msgs[i]);

	exit(0);
}
void enable_ctrl_c_handler() {
	signal(SIGINT, clean_exit);
	signal(SIGKILL, clean_exit);
	signal(SIGABRT, clean_exit);
	signal(SIGALRM, clean_exit);
	signal(SIGBUS, clean_exit);
	signal(SIGCHLD, clean_exit);
	signal(SIGCONT, clean_exit);
	signal(SIGFPE, clean_exit);
	signal(SIGHUP, clean_exit);
	signal(SIGILL, clean_exit);
	signal(SIGPIPE, clean_exit);
	signal(SIGQUIT, clean_exit);
	signal(SIGSEGV, clean_exit);
	signal(SIGSTOP, clean_exit);
	signal(SIGTERM, clean_exit);
	signal(SIGTSTP, clean_exit);
	signal(SIGTTIN, clean_exit);
	signal(SIGTTOU, clean_exit);
	signal(SIGUSR1, clean_exit);
	signal(SIGUSR2, clean_exit);
	signal(SIGPROF, clean_exit);
	signal(SIGSYS, clean_exit);
	signal(SIGTRAP, clean_exit);
	signal(SIGURG, clean_exit);
	signal(SIGVTALRM, clean_exit);
	signal(SIGXCPU, clean_exit);
	signal(SIGXFSZ, clean_exit);

}
